
import java.net.*; 
import java.io.*; 
import java.util.*;

import common.RobotInformation;
import common.Shared;



public class FTP_Client
{	
	private final static int 
			DATA_PORT = 8265;
	
	private static final int
			SERVER_CONTROL = 0,
			PEER_CONTROL   = 1;
	
	private static int clientServerPort; // Make random
	
	private static Socket 
			control[] = new Socket[ 2 ];
	
	private static Socket 
			data;				// only connects to other clients

	protected static String
			line     = null,
			parsed[] = null;
	
	private static DataInputStream
			in [] = new DataInputStream [ 2 ]; 
	private static DataOutputStream
			out[] = new DataOutputStream[ 2 ];
	
	private static DataInputStream
			dataIn;
	
	private static DataOutputStream
			dataOut;
	
	private static Vector<HitInformation> recentQuery = 
			new Vector<HitInformation>(0);
	
	
	FTP_Client( int serverPort ){
		clientServerPort = serverPort;
	}
	/** r u n (   )
	 * ------------------------------------------------------------------------
	 *   Sets up the client's user interface, parses entered commands, and runs
	 *   the appropriate method for each case.
	 */
	public void handleInput(String inputLine)
	{

			line = inputLine;
			parsed = line.split( " " );
			parsed[ Shared.COMMAND ] = parsed[ Shared.COMMAND ].toUpperCase();
	
			if( parsed[ Shared.COMMAND ].equals( Shared.RETRIEVE ) )
			{
				handleRETRIEVE( );
				return;
			}

			if( control[ SERVER_CONTROL ] != null &&
			    parsed[ Shared.COMMAND ].equals( Shared.QUERY ) )
			{
				handleQUERY();
				return;
			}
			if( control[ SERVER_CONTROL ] != null &&
					parsed[ Shared.COMMAND ].equals( Shared.ROBOT ) )
			{
				handleROBOT();
				return;
			}

			if( parsed[ Shared.COMMAND ].equals( Shared.CONNECT ) )
			{
				System.out.println("Connecting to Server");
				handleCONNECT( SERVER_CONTROL );
				return;
			}
			else if( parsed[ Shared.COMMAND ].equals( Shared.QUIT ) )
			{
				if( handleQUIT( SERVER_CONTROL ) )
					System.exit(0);
				return;
			}
			
			System.out.println
					( " -= NOTE: " + 
					  parsed[ Shared.COMMAND ] + 
					  " is an invalid command" + 
					  ( control[ SERVER_CONTROL ] == null ? 
					  		", or you are not connected to any server." :
					  		"." ) );
		}
	/** h a n d l e S T O R E a n d R E T R I E V E (   )
	 * ------------------------------------------------------------------------
	 *   Handles the STOR and RETR commands.
	 */
	private static void handleRETRIEVE()
	{
		if( parsed.length < 3 )
		{
			System.out.println
					( " -= NOTE: Usage - " +
					  parsed[ Shared.COMMAND ] +
					  " < Client # > < File # > " );
			return;
		}
		
		int client = Integer.parseInt(parsed[ Shared.CLIENTNUM ]);
		int file = Integer.parseInt(parsed[ Shared.FILENUM ]);

		if( client >= recentQuery.size() )
		{
			Shared.error( "Client number " + client + " does not exist!" );
			return;
		}
		if( file >= recentQuery.elementAt( client ).getNumHits() )
		{
			Shared.error( "File number " + file + " on client " + client +
						  " does not exist!" );
			return;
		}
 
		String request = 
				Shared.RETRIEVE + 
				" " + 
				recentQuery.elementAt(client).getFileName( file );
		
		InetAddress destIP   = recentQuery.elementAt( client ).getDestIP();
		int		    destPort = recentQuery.elementAt( client ).getDestPort(); 
		
		HitInformation result = null;
		try {
			control[ PEER_CONTROL ] = 
					new Socket( destIP, 
								destPort );
		} catch( IOException e ){
			System.out.println( " ** ERROR: Could not query client \"" +
					destIP + 
					"\" on port " +
					destPort +
					"! **");
			return;
		}
		
		try {
			in [ Shared.DATA_LINE ] =
				new DataInputStream	(control[ PEER_CONTROL ].getInputStream());
			out[ Shared.DATA_LINE ] = 
				new DataOutputStream(control[ PEER_CONTROL ].getOutputStream());
		} catch( IOException e ){}

		try {
			out[ Shared.DATA_LINE ].writeUTF( request );
			out[ Shared.DATA_LINE ].writeInt( DATA_PORT );
		} catch( IOException e )
		{
			Shared.error( " ** ERROR: Could not retrieve file from peer. **" );
			return;
		}
		
		ServerSocket dataSocket = null;
		
		try {
			dataSocket = new ServerSocket( DATA_PORT );
		} catch( IOException e )
		{
			System.out.println
					( " ** FATAL ERROR: Could not create " + 
					  "a server socket! Aborting. **" );
			System.exit( 1 );
		}

		System.out.print( " -- Waiting for server to connect..." );
		
		try {
			data = dataSocket.accept();
		} catch( IOException e ) {
			controlLineError( PEER_CONTROL );
			// should be data line error
		}
		
		Shared.closeObject( dataSocket );

		try {
			Shared.retrieveFile
					( Shared.DOWNLOADED + recentQuery.elementAt(client).getFileName( file ), 
					  dataIn = new DataInputStream( data.getInputStream() ) );
		} catch( IOException e ) {
			System.out.println
					( " ** ERROR: Could not get the data " +
					  "stream from the data connection! **" );
		}
		
		try {
			out[ Shared.DATA_LINE ].writeUTF( Shared.QUIT );
			Shared.closeObject( control[ PEER_CONTROL ] );
		} catch( IOException e ){
			System.out.println( "data line error");
		}
	}
	
	/** h a n d l e L I S T (   )
	 * ------------------------------------------------------------------------
	 *   Handles the LIST command. 
	 */
	private static void handleQUERY()
	{
		RobotInformation list = null;
		
		if( parsed.length < 2 ){
			System.out.println("Usage: query <filename> ");
			return;
		}
		
		try {
			out[ Shared.CONTROL_LINE ].writeUTF( line );
			list = Shared.getHit( in[ Shared.CONTROL_LINE ] );
		} catch( IOException e ) {
			controlLineError( SERVER_CONTROL );
		}
		
		//System.out.println( " [ Directory Listing ]");
		//if( list.isEmpty() ){
		//	System.out.println( "\tNo Matching Files" );
		//}else{
		//	for( int x = 0; x < list.size(); x++ ){
		//		System.out.println( " Client " + x + "\t" + list.elementAt(x) );
		//	}
			//recentQuery = list;
		//}
		Shared.roboInfo = list;
	}
	/** h a n d l e ROBOT(   )
	 * ------------------------------------------------------------------------
	 *   Handles the ROBOT command. 
	 */
	private static void handleROBOT()
	{
		Vector<HitInformation> list = null;
		
//		try {
//			out[ Shared.CONTROL_LINE ].writeUTF( line );
//			list = Shared.getHitList( in[ Shared.CONTROL_LINE ] );
//		} catch( IOException e ) {
//			controlLineError( SERVER_CONTROL );
//		}
//		
//		System.out.println( " [ Directory Listing ]");
//		if( list.isEmpty() ){
//			System.out.println( "\tNo Matching Files" );
//		}else{
//			for( int x = 0; x < list.size(); x++ ){
//				System.out.println( " Client " + x + "\t" + list.elementAt(x) );
//			}
//			recentQuery = list;
//		}
	}

	private static void handleCONNECT
			( int lineNum )
	{
		if( parsed.length < 3 ) {
			System.out.println( " -= NOTE: Usage - CONNECT <server> <port>" );
			return;
		}

		InetAddress destInetAddress;
		try {
			destInetAddress = InetAddress.getByName( parsed[ Shared.SERVER ] );
		} catch( UnknownHostException e ) {
			Shared.error( "Specified host could not be resolved!" );
			return;
		}
		int destPort = 
				Integer.parseInt( parsed[ Shared.PORT ] );

		handleCONNECT( destInetAddress,
					   destPort,
					   lineNum );
	}

	/** h a n d l e C O N N E C T (   )
	 * ------------------------------------------------------------------------
	 *   Handles the CONNECT command.
	 */
	private static void handleCONNECT
			( InetAddress destInetAddress, 
			  int 		  destPort,
			  int 		  lineNum )
	{
		try {
			control[ lineNum ] = 
					new Socket( destInetAddress, 
								destPort );
		} catch( IOException e )
		{
			System.out.println( " ** ERROR: Could not connect to \"" +
								destInetAddress + 
								"\" on port " +
								destPort +
								"! **");
			return;
		}

		try {
			in [ lineNum ] = 
					new DataInputStream ( control[ lineNum ].getInputStream()  );
			
			out[ lineNum ] = 
					new DataOutputStream( control[ lineNum ].getOutputStream() );
			
			out[ lineNum ].writeInt( clientServerPort );
			
			System.out.println( " -- Connected to \"" + 
								destInetAddress + 
								"\" on port " +
								destPort +
								"." );
		} catch( IOException e ) {
			controlLineError( lineNum );
		}
	}
	
	/** h a n d l e Q U I T (   )
	 * ------------------------------------------------------------------------
	 *   Handles the QUIT command. 
	 */
	private static boolean handleQUIT
			( int lineNum )
	{
		if( control[ lineNum ] != null )
		{
			System.out.println( " -- Disconnecting from \"" +
					control[ lineNum ].getInetAddress() + 
					"\" on port " +
					control[ lineNum ].getPort() + 
					"." );

			try {
				out[ lineNum ].writeUTF( Shared.QUIT );
			} catch( IOException e ) {
				controlLineError( lineNum );
			}
			
			closeControlLine( lineNum );
			return false;
		}
		
		return true;
	}

	/** c o n t r o L i n e E r r o r (   )
	 * ------------------------------------------------------------------------
	 *   Prints out an error message, then calls the method to close the
	 *   control line. 
	 */
	private static void controlLineError
			( int lineNum )
	{
		Shared.error( "Control line error! Aborting connection with server." );
		closeControlLine( lineNum );
	}
	
	/** c l o s e C o n t r o l L i n e (   )
	 * ------------------------------------------------------------------------
	 *   Closes the control line streams as well as the socket itself. 
	 */
	private static void closeControlLine
			( int lineNum )
	{
		Shared.closeObject( control[ lineNum ] );
		control[ lineNum ] = null;

		Shared.closeObject( in [ lineNum ] );
		Shared.closeObject( out[ lineNum ] );
	}
}
